package com.flying.invest.strategy.service;

import java.util.Date;

import com.flying.common.annotation.DaoCreate;
import com.flying.common.annotation.DaoQuery;
import com.flying.common.annotation.DaoRemove;
import com.flying.common.annotation.DaoUpdate;
import com.flying.common.annotation.Transaction;
import com.flying.common.util.Codes;
import com.flying.common.util.DateUtils;
import com.flying.common.util.DateUtils.Field;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.model.QueryResult;
import com.flying.framework.service.AbstractService;
import com.flying.invest.invest.TransactionType;
import com.flying.invest.invest.model.InvestmentSuggestion;
import com.flying.invest.invest.model.InvestmentTransaction.Status;
import com.flying.invest.invest.service.InvestmentService;
import com.flying.invest.invest.service.InvestmentSuggestionService;
import com.flying.invest.invest.service.InvestmentTransactionService;
import com.flying.invest.strategy.model.StrategyAutoPlan;
import com.flying.invest.strategy.model.StrategyAutoPlan.CommissionType;
import com.flying.invest.strategy.model.StrategyAutoPlan.UnitType;

@Service(value="StrategyAutoPlanService", desc="定投")
public class StrategyAutoPlanService extends AbstractService implements IStrategyService, ITransactionCallbackService{
	private InvestmentService investService;
    private InvestmentTransactionService transactionService;
    private StrategyService strategyService;
    private InvestmentSuggestionService suggestionService;
    private StrategyAutoPlanDetailService detailService;
    
    public void setInvestmentService(InvestmentService s) {
    	this.investService = s;
    }
    public void setInvestmentTransactionService(InvestmentTransactionService s) {
    	this.transactionService = s;
    }
    public void setStrategyService(StrategyService s) {
    	this.strategyService = s;
    }
    public void setInvestmentSuggestionService(InvestmentSuggestionService s) {
    	this.suggestionService = s;
    }
    public void setStrategyAutoPlanDetailService(StrategyAutoPlanDetailService s) {
    	this.detailService = s;
    }
        
	@MethodInfo("新增")
	@DaoCreate(entity=StrategyAutoPlan.ENTITY_NAME)
	public StrategyAutoPlan create(
			@Param(value=StrategyAutoPlan.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyAutoPlan.PLAN_START, required=true,	desc="计划开始") Date plan_start,
			@Param(value=StrategyAutoPlan.PLAN_END, required=false,	desc="计划结束") Date plan_end,
			
			@Param(value=StrategyAutoPlan.PLAN_INTERVAL, required=true,	desc="计划间隔") Integer plan_interval,
			@Param(value=StrategyAutoPlan.PLAN_UNIT, required=true,	desc="间隔单位", enumClass=UnitType.class) Integer plan_unit,
			@Param(value=StrategyAutoPlan.TOTAL_NUM, required=false,	desc="投入期数") Integer total_num,
			
			@Param(value=StrategyAutoPlan.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type,
			@Param(value=StrategyAutoPlan.AMOUNT, required=false,	desc="买入金额") Double amount,
			@Param(value=StrategyAutoPlan.QUANTITY, required=false,	desc="买入数量") Double quantity
		) {
		
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public StrategyAutoPlan createSub(Long strategy_id) {
		var s = this.strategyService.findById(strategy_id);
		var v = this.investService.findById(s.getInvestId());
		var quantity = 1000.0;
		return this.create(strategy_id, new Date(), null, 
				1, UnitType.DOUBLE_WEEK.value, 10, 
				CommissionType.AMOUNT.value, (v.getPrice() == null?0:v.getPrice()) * quantity, quantity);
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=StrategyAutoPlan.ENTITY_NAME)
	public ActionResult update(
			@Param(value=StrategyAutoPlan.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyAutoPlan.PLAN_START, required=true,	desc="计划开始") Date plan_start,
			@Param(value=StrategyAutoPlan.PLAN_END, required=false,	desc="计划结束") Date plan_end,
			
			@Param(value=StrategyAutoPlan.PLAN_INTERVAL, required=true,	desc="计划间隔") Integer plan_interval,
			@Param(value=StrategyAutoPlan.PLAN_UNIT, required=true,	desc="间隔单位", enumClass=UnitType.class) Integer plan_unit,
			@Param(value=StrategyAutoPlan.TOTAL_NUM, required=false,	desc="投入期数") Integer total_num,
			
			@Param(value=StrategyAutoPlan.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type,
			@Param(value=StrategyAutoPlan.AMOUNT, required=false,	desc="买入金额") Double amount,
			@Param(value=StrategyAutoPlan.QUANTITY, required=false,	desc="买入数量") Double quantity
		) {
		
		return null;
	}

	@MethodInfo("修改")
	public ActionResult config(
			@Param(value=StrategyAutoPlan.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyAutoPlan.PLAN_START, required=true,	desc="计划开始") Date plan_start,
			@Param(value=StrategyAutoPlan.PLAN_END, required=false,	desc="计划结束") Date plan_end,
			
			@Param(value=StrategyAutoPlan.PLAN_INTERVAL, required=true,	desc="计划间隔") Integer plan_interval,
			@Param(value=StrategyAutoPlan.PLAN_UNIT, required=true,	desc="间隔单位", enumClass=UnitType.class) Integer plan_unit,
			@Param(value=StrategyAutoPlan.TOTAL_NUM, required=false,	desc="投入期数") Integer total_num,
			
			@Param(value=StrategyAutoPlan.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type,
			@Param(value=StrategyAutoPlan.AMOUNT, required=false,	desc="买入金额") Double amount,
			@Param(value=StrategyAutoPlan.QUANTITY, required=false,	desc="买入数量") Double quantity
		) {
		try {
			this.update(strategy_id, plan_start, plan_end, plan_interval, plan_unit, total_num, commission_type, amount, quantity);
		} catch (Exception e) {
			this.create(strategy_id, plan_start, plan_end, plan_interval, plan_unit, total_num, commission_type, amount, quantity);
		}
		this.flushDetails(strategy_id);
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

	@MethodInfo("删除")
	@DaoRemove(entity=StrategyAutoPlan.ENTITY_NAME)
	public ActionResult remove(
			@Param(value=StrategyAutoPlan.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		this.detailService.removeByStrategyId(strategy_id);
		return null;
	}

	@MethodInfo("搜索")
	@DaoQuery(entity=StrategyAutoPlan.ENTITY_NAME, pageable=true, modelClass=StrategyAutoPlan.class)
	public QueryResult<StrategyAutoPlan> findAll(
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) {
		return null;
	}

	@SuppressWarnings("unchecked")
	@MethodInfo("查询")
	@DaoQuery(entity=StrategyAutoPlan.ENTITY_NAME, single=true, throwsNotFoundException=true )
	public StrategyAutoPlan findById(
			@Param(value=StrategyAutoPlan.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		return null;
	}
	
	//
	@Override
	public ActionResult transactionCallback(Long strategy_id, Long transaction_id, Integer transaction_status,
			Integer transaction_type, Double quantity, Double price, Double amount) {
		if(transaction_status != Status.SUCCESS.value()) {
			return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
		}
		var transaction = this.transactionService.findById(transaction_id);
		if(transaction.getSuggestionId() != null) {
			var suggestion = this.suggestionService.findById(transaction.getSuggestionId());
			var detail = this.detailService.findById(Long.parseLong(suggestion.getExtra()));
			this.detailService.updateByTransaction(detail.getDetailId(), transaction.getTransactionDate(), 
					transaction.getAmount(), transaction.getQuantity(), transaction.getPrice(), transaction.getTransactionId());
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	
	@Override
	public ActionResult executeStrategy(
			@Param(value=StrategyAutoPlan.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		var strategy = this.strategyService.findById(strategy_id);
		var invest = this.investService.findById(strategy.getInvestId());
		var details = this.detailService.findByStrategyId(strategy_id).getRows();
		var suggestions = this.suggestionService.findByStrategyId(strategy_id, null).getRows();
		details.stream().filter(d -> d.getTransactionId() == null && d.getPlanDate().compareTo(new Date()) <= 0).forEach(d -> {
			var found = suggestions.stream().anyMatch(s -> 
					s.getExtra() != null && d.getDetailId() == Long.parseLong(s.getExtra()) &&
					(s.getStatus() == InvestmentSuggestion.Status.ORDERED.value() || s.getStatus() == InvestmentSuggestion.Status.PREPARING.value()));
			if(!found) {
				var inv = new Invest(d.getPlanAmount(), invest.getPrice(), 100.0);
				var remarks = "当前日期"+ DateUtils.formatDate(new Date(), "yyyy-MM-dd")+"，满足定投设定日期"+DateUtils.formatDate(d.getPlanDate(), "yyyy-MM-dd")+"，建议买入！";
				this.suggestionService.create(invest.getUserId(), invest.getInvestId(), strategy_id, 
						d.getNum(), invest.getAssetsName(), remarks, String.valueOf(d.getDetailId()), 
						TransactionType.BUY.value, inv.getAmount(), inv.getQuantity(), 
						new Date(), DateUtils.add(new Date(), Field.DATE, 5), InvestmentSuggestion.Status.PREPARING.value, "system", new Date());
			}
		});
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	
	@Override
	@Transaction
	public ActionResult flushDetails(
			@Param(value=StrategyAutoPlan.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		var details = this.detailService.findByStrategyId(strategy_id).getRows();
		var found = details.stream().anyMatch(d -> d.getTransactionId() != null);
		if(found) {
			throw new AppException("0101", "已经有成功交易，不能修改，建议禁用后重新创建！");
		}
		this.detailService.removeByStrategyId(strategy_id);
		//
		var strategy = this.strategyService.findById(strategy_id);
		var invest = this.investService.findById(strategy.getInvestId());
		var plan = this.findById(strategy_id);
		var num = 1;
		var date = plan.getPlanStart();
		while(plan.getTotalNum() != null && num < plan.getTotalNum() || 
				plan.getPlanEnd() != null && date.compareTo(plan.getPlanEnd()) < 0) {
			var plan_amount = plan.getAmount() != null?plan.getAmount(): plan.getQuantity() * invest.getPrice();
			var plan_quantity = plan.getQuantity() != null? plan.getQuantity(): plan_amount / invest.getPrice();
			this.detailService.create(invest.getUserId(), strategy.getTypeId(), invest.getInvestId(), strategy_id, 
					num, date, plan_amount, plan_quantity);
			num ++;
			
			var unit = UnitType.member(plan.getPlanUnit());
			switch (unit) {
			case DAY:
				date = DateUtils.add(date, Field.DATE, plan.getPlanInterval());
				break;
			case WORK_DAY:
				date = DateUtils.add(date, Field.DATE, plan.getPlanInterval());
				break;
			case WEEK:
				date = DateUtils.add(date, Field.DATE, plan.getPlanInterval() * 7);
				break;
			case DOUBLE_WEEK:
				date = DateUtils.add(date, Field.DATE, plan.getPlanInterval() * 7 * 2);
				break;
			case MONTH:
				date = DateUtils.add(date, Field.MONTH, plan.getPlanInterval());
				break;
			case SEASON:
				date = DateUtils.add(date, Field.MONTH, plan.getPlanInterval() * 3);
				break;
			case YEAR:
				date = DateUtils.add(date, Field.YEAR, plan.getPlanInterval());
				break;
			}
		}
		//
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

}